/**
 *
 */
package com.wedroid.r2d2.message.command;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.wedroid.entity.command.Command;
import com.wedroid.entity.command.TemplateType;
import com.wedroid.r2d2.contact.Contact;
import com.wedroid.r2d2.http.RequestManager;

/**
 * @author lute
 */
@Component("imageCommandOperator")
public class ImageCommandOperator extends TemplateCommandOperator {

    private static final String COMMAND_IMAGE_PATH = "/command/image";

    @Value("#{config['resource.path']}")
    private String resourcePath;

    @Autowired
    private RequestManager requestManager;

    /* (non-Javadoc)
     * @see com.wedroid.r2d2.message.command.CommandOperator#supportsCommand(com.wedroid.entity.command.TemplateType)
     */
    public boolean supportsCommand(TemplateType templateType) {
        return TemplateType.IMAGE.equals(templateType);
    }

    /* (non-Javadoc)
     * @see com.wedroid.r2d2.message.command.TemplateCommandOperator#doOperate(com.wedroid.entity.command.Command, java.lang.String, com.wedroid.r2d2.contact.Contact)
     */
    @Override
    protected void doOperate(Command command, String template, Contact sender) {
        Path path = null;

        if (template.startsWith("http")) {
            // Check local directory contains image or not
            String today = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
            Path directory = Paths.get(resourcePath, COMMAND_IMAGE_PATH, today);
            if (Files.exists(directory) && Files.isDirectory(directory) && Files.isReadable(directory)) {
                String fileName = command.getId() + "_" + command.getKeyword();
                try {
                    List<File> files = Files.list(directory)
                                            .filter(Files::isReadable)
                                            .filter(Files::isRegularFile)
                                            .map(Path::toFile)
                                            .filter(file -> StringUtils.equals(file.getName(), fileName))
                                            .collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(files)) {
                        logger.trace("#doOperate - Get file [{}] form local disk", path);
                        path = files.get(0).toPath();
                    }
                } catch (IOException e) {
                    logger.error("#doOperate - Tranversing directory [{}] encounters IOException: {}", directory, e);
                }
            }

            // No image found in local disk, go to download it
            if (path == null) {
                Optional<Path> downloadedPath = downloadImage(command, template);
                if (downloadedPath.isPresent()) {
                    logger.trace("#doOperate - Download image to local disk [{}]", downloadedPath.get());
                    path = downloadedPath.get();
                }
            }
        } else {
            path = Paths.get(template);
        }

        if (Files.exists(path) && Files.isRegularFile(path) && Files.isReadable(path)) {
            if (isImage(path)) {
                requestManager.sendImage(path.toString(), sender.getUserName());
            } else {
                logger.error("#doOperate - The file specified by path [{}] is not image", path);
            }
        } else {
            logger.error("#doOperate - path [{}] has something wrong...", path);
        }
    }

    /**
     * @param path
     * @return
     */
    private boolean isImage(Path path) {
        File file = path.toFile();
        String mimeType = URLConnection.guessContentTypeFromName(file.getName());
        return mimeType.startsWith("image");
    }


    /**
     * @param command
     * @param url
     * @return
     */
    private Optional<Path> downloadImage(Command command, String url) {
        try {
            Response response  = Request.Get(url).execute();
            HttpResponse httpResponse = response.returnResponse();

            Header header = httpResponse.getFirstHeader(HttpHeaders.CONTENT_TYPE);
            String contentType = header != null ? header.getValue() : "";
            if (contentType.startsWith("image")) {
                HttpEntity entity = httpResponse.getEntity();
                if (entity != null) {
                    String today = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
                    String fileName = command.getId()+ "_" + command.getKeyword() + "." + contentType.split("/")[1];

                    Path path = Paths.get(resourcePath, COMMAND_IMAGE_PATH, today, fileName);
                    try (InputStream inputStream = entity.getContent()) {
                        FileUtils.copyToFile(inputStream, path.toFile());
                        return Optional.of(path);
                    }
                } else {
                    logger.error("#downloadImage - Get null entity");
                }
            } else {
                logger.error("#downloadImage - Downloaded file has the content type of [{}]", contentType);
            }
        } catch (ClientProtocolException e) {
            logger.error("#downloadImage - Downloading image from [{}] encounters ClientProtocolException: {}", url, e);
        } catch (IOException e) {
            logger.error("#downloadImage - Downloading image from [{}] encounters IOException: {}", url, e);
        }
        return Optional.empty();
    }

}
